
//import gov.nist.javax.sip.stack.MessageProcessor;
import java.util.HashMap;
import java.util.Properties;
import java.util.TooManyListenersException;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.PeerUnavailableException;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipFactory;
import javax.sip.SipListener;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TimeoutEvent;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.TransportNotSupportedException;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.ContactHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ToHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

/** Sip class
 * 
 * "the most important methods evidently
 *  are processRequest() and processResponse()
 *  for processing incoming messages."
 *
 * @author David Rodriguez A.
 * 
 */

public class Sip implements SipListener{
    private String          user;               /*User name*/
    private SipStack        sipStack;           /*Stack Sip*/
    private SipFactory      sipFactory;
    private AddressFactory  addressFactory;
    private HeaderFactory   headerFactory;
    private MessageFactory  messageFactory;
    private SipProvider     sipProvider;
    HashMap<String, Integer> type;              /*HashMap que contendra los tipos eventos */

    Valve valve;
    AppValveViewer console; //para ver la consola y actualizarla
    /**
     * Sip Constructor:
     *
     * @param  user
     * @param  ip
     * @param  port
     * @throws PeerUnavailableException
     * @throws TransportNotSupportedException
     * @throws InvalidArgumentException
     * @throws ObjectInUseException
     * @throws TooManyListenersException
     */

    public Sip(String user, String ip, int  port, Valve val, String stackname,AppValveViewer console) throws PeerUnavailableException,TransportNotSupportedException,
                             InvalidArgumentException, ObjectInUseException,
                             TooManyListenersException {
        valve  = val;
        this.console = console;
        //System.out.println("Sip.java >>> Sip Object Created");
        console.setArea("Sip.java >>> Sip Object Created\n");
        setUser(user);
        type = new HashMap<String,Integer>(); //mapa con los tipos de eventos
        initMap(); //inicia mapa de eventos
        //crea la instancia sip
        sipFactory = SipFactory.getInstance();
        //console.setArea(sipFactory.getPathName());
        //System.out.println(sipFactory.getPathName());

        //objeto properties que contendra información del usuario y logs========
        Properties properties = new Properties();
        properties.setProperty("javax.sip.STACK_NAME",stackname);
        properties.setProperty("javax.sip.IP_ADDRESS", ip);

        //DEBUGGING: La informacion debugeada ira a los archivos
	//textclient.log y textclientdebug.log
        properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
        properties.setProperty("gov.nist.javax.sip.SERVER_LOG","textclient.txt");
        properties.setProperty("gov.nist.javax.sip.DEBUG_LOG","textclientdebug.log");
        this.console.setArea("Creating Stack....\n");
        this.console.setArea("Stack Name:"+properties.getProperty("javax.sip.STACK_NAME")+"\n");
        this.console.setArea("Stack Ip:"+properties.getProperty("javax.sip.IP_ADDRESS")+"\n");

        //======================================================================

        //crea y deja levantado el stack SIP====================================
        sipStack = sipFactory.createSipStack(properties);
        
        //System.out.println("(Sip.java) >>> sipStack:ip "+ sipStack.getIPAddress());
        headerFactory = sipFactory.createHeaderFactory();
        addressFactory = sipFactory.createAddressFactory();
        messageFactory = sipFactory.createMessageFactory();
        //======================================================================

        //crea puntos de escucha para protocolo TCP y UDP

        // corregido para JAIN-SIP-1.2 (metodos deprecados) - @dgongora
        ListeningPoint tcp = sipStack.createListeningPoint(ip, port, "tcp");
        ListeningPoint udp = sipStack.createListeningPoint(ip, port, "udp");

        //agrega los protocolos TCP y UDP al stack sip
        sipProvider = sipStack.createSipProvider(tcp);
	sipProvider.addSipListener(this);
	sipProvider = sipStack.createSipProvider(udp);
	sipProvider.addSipListener(this);
    }//end_constructor

    //inicia mapa con los eventos que se puede recibir
    public void initMap(){
        type.put("MESSAGE", 0); //para MESSAGE, valor 0
        type.put("INVITE", 1);  //para invite, valor 1
        type.put("ACK", 2);     //para ACK, valor 2
        type.put("BYE", 3);     //para BYE, valor 3
        type.put("CANCEL", 4);  //para CANCEL, valor 4
        type.put("REGISTER", 5);//para REGISTER, valor 5
        System.out.println("MapEvent: "+type);
    }
    
    public void processRequest(RequestEvent re) {
        //System.out.println("(Sip.java)[processRequest(RequestEvent re)]");
        this.console.setArea("(Sip.java)[processRequest(RequestEvent re)]\n");
        Request req = re.getRequest();
        
        switch(type.get(req.getMethod())){
            case 0:
                //System.out.println("<<<<<<<<<<<< MESSAGE <<<<<<<<<<<<");
                break;
            case 1://INVITE
                //System.out.println("<<<<<<<<<<<< INVITE <<<<<<<<<<<<");
                this.console.setArea("<<<<<<<<<<<< INVITE <<<<<<<<<<<<\n");
                printDatos(req); //imprime datos del request
                //Responder 2xxOK
                buildMessageResponse(200, req);
                break;
            case 2://ACK
                //System.out.println("<<<<<<<<<<<< ACK <<<<<<<<<<<<");
                this.console.setArea("<<<<<<<<<<<< ACK <<<<<<<<<<<<\n");
                
                //Iniciar Sesión de Media
                break;
            case 3://BYE
                //System.out.println("<<<<<<<<<<<< BYE <<<<<<<<<<<<");
                this.console.setArea("<<<<<<<<<<<< BYE <<<<<<<<<<<<\n");
                //System.out.println("parando hilo");
                if(valve.getLess()) valve.pauseOff();
                else valve.pauseOn();//para el reactor, bajara temperatura automaticamente               
                break;
            case 4: //CANCEL
                //System.out.println("<<<<<<<<<<<< CANCEL <<<<<<<<<<<<");
                this.console.setArea("<<<<<<<<<<<< CANCEL <<<<<<<<<<<<\n");
                break;
            case 5:
                //System.out.println("<<<<<<<<<<<< REGISTER <<<<<<<<<<<<");
                this.console.setArea("<<<<<<<<<<<< REGISTER <<<<<<<<<<<<\n");
                break;
            default:
                //Error
        }
    }//end public void processRequest()
    
    public void buildMessageResponse(int type, Request req){
        try {
            Response response = null;
            response = messageFactory.createResponse(type, req);
            ToHeader toHeader = (ToHeader) response.getHeader(ToHeader.NAME);
            toHeader.setTag("888"); //Esto es obligatorio segun la especificacion
            ServerTransaction st = sipProvider.getNewServerTransaction(req);

            //crea Contact-Header (Obligatorio)=================================
            SipURI contactURI = addressFactory.createSipURI(getUser(),getHost());
            contactURI.setPort(getPort());
            Address contactAddress = addressFactory.createAddress(contactURI);
            contactAddress.setDisplayName(getUser());
            ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
            response.addHeader(contactHeader);
            //System.out.println("response: \n" + response);
            this.console.setArea("_______________________________________________________________\n");
            this.console.setArea("Creating Response......\n");
            this.console.setArea("response: " + response+"\n");
            this.console.setArea("_______________________________________________________________\n");
            //==================================================================
            //envia el response
            response.setStatusCode(type); //para el 200ok es 200
            //System.out.println(">>>>>>>>>>>> Sending response >>>>>>>>>>>>");
            this.console.setArea(">>>>>>>>>>>> Sending response >>>>>>>>>>>>\n");
            st.sendResponse(response);
        }catch ( Throwable e){
            //System.out.println("Can't send OK reply.");
            this.console.setArea("Can't send OK reply.\n");
            e.printStackTrace();
            
        }
    }
    /**
     * 
     * @param evt
     */
    public void processResponse(ResponseEvent evt) {
        Response response = evt.getResponse();
        int status = response.getStatusCode();

        if ((status >= 200) && (status < 300)) { //Success!
            //System.out.println("success");
            this.console.setArea("Success!!!\n");
            return;
        }
        //System.out.println("Previous message not sent: " + status);
        this.console.setArea("Previous message not sent: " + status+"\n");
    }


    public void processTimeout(TimeoutEvent te) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void processIOException(IOExceptionEvent ioee) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void processTransactionTerminated(TransactionTerminatedEvent tte) {
        System.out.println("(Sip.java) [processTransactionTerminated()]");
    }

    public void processDialogTerminated(DialogTerminatedEvent dte) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /*Set the user name*/
    public void setUser(String user){
        this.user = user;
    }

   /* public void setMessageProcessor(MessageProcessor newMessageProcessor) {
	messageProcessor = newMessageProcessor;
    }*/

    public String getHost() {
    	// port doesn't needed here. - @dgongora
        //int port = sipProvider.getListeningPoint().getPort();
        String host = sipStack.getIPAddress();
        return host;
    }

    public int getPort() {
        // corregido para JAIN-SIP-1.2 (m�todos deprecados) - @dgongora
    	int port = sipProvider.getListeningPoints()[0].getPort();
        //int port = sipProvider.getListeningPoint().getPort();
        return port;
    }

    public String getUser() {
        return user;
    }


    /**
     *
     * @param req
     * "Se cambiaron todos los println por setArea de la consola implementada"
     */
    public void printDatos(Request req){
        this.console.setArea("(Sip.java)[printDatos()]>>> TO         : "+req.getRequestURI()+"\n");
        this.console.setArea("(Sip.java)[printDatos()]>>> FROM       : "+((FromHeader)req.getHeader("From")).getAddress().getURI()+"\n");
        this.console.setArea("(Sip.java)[printDatos()]>>> SIPversion : "+ req.getSIPVersion()+"\n");
        this.console.setArea("(Sip.java)[printDatos()]>>> Expires    : "+ req.getExpires().getExpires()+"\n");
        this.console.setArea("(Sip.java)[printDatos()]>>> Contact    : "+ (((ContactHeader)req.getHeader("Contact")).getAddress().getURI())+"\n");
        
    }



}


/** Session Initiation Protocol
 * (SIP o Protocolo de Inicio de Sesiones)
 */
